/**
 * Copyright (c) 2009, Robert Schuster
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 *
 * - Redistributions of source code must retain the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer.
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer in the documentation and/or other
 *   materials provided with the distribution.
 * - Neither the name of Robert Schuster nor the names of his
 *   contributors may be used to endorse or promote products
 *   derived from this software without specific prior written
 *   permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This Class is the Interface to the C-Functions declared in CLcalc.h
 */

package jclcalc;

/**
 *
 * @author robert schuster
 */
public class JCLcalc {

    //Direction for the Arguments

    /**
     * This flag specifies that the memory object will be read and written by a kernel.
     */
    public final static int CL_MEM_READ_WRITE = (1 << 0);
    /**
     * This flags specifies that the memory object will be written but not read by a kernel.
     * <p>Reading from a buffer or image object created with CL_MEM_WRITE_ONLY inside a kernel is undefined.
     */
    public final static int CL_MEM_WRITE_ONLY = (1 << 1);
    /**
     * This flag specifies that the memory object is a read-only memory object when used inside a kernel.
     * <p>Writing to a buffer or image object created with CL_MEM_READ_ONLY inside a kernel is undefined.
     */
    public final static int CL_MEM_READ_ONLY = (1 << 2);
    /**
     * This flag is valid only if host_ptr is not NULL. If specified, it indicates that the application wants the OpenCL implementation to use memory referenced by host_ptr as the storage bits for the memory object.
     * <p>OpenCL implementations are allowed to cache the buffer contents pointed to by host_ptr in device memory. This cached copy can be used when kernels are executed on a device.
     * <p>The result of OpenCL commands that operate on multiple buffer objects created with the same host_ptr or overlapping host regions is considered to be undefined.
     */
    public final static int CL_MEM_USE_HOST_PTR = (1 << 3);
    /**
     * This flag specifies that the application wants the OpenCL implementation to allocate memory from host accessible memory.
     * <p>CL_MEM_ALLOC_HOST_PTR and CL_MEM_USE_HOST_PTR are mutually exclusive.
     */
    public final static int CL_MEM_ALLOC_HOST_PTR = (1 << 4);
    /**
     * This flag is valid only if host_ptr is not NULL. If specified, it indicates that the application wants the OpenCL implementation to allocate memory for the memory object and copy the data from memory referenced by host_ptr.
     * <p>CL_MEM_COPY_HOST_PTR and CL_MEM_USE_HOST_PTR are mutually exclusive.
     * <p>CL_MEM_COPY_HOST_PTR can be used with CL_MEM_ALLOC_HOST_PTR to initialize the contents of the cl_mem object allocated using host-accessible (e.g. PCIe) memory.
     */
    public final static int CL_MEM_COPY_HOST_PTR = (1 << 5);

    /*
     * Device type Contants
     */
    public final static int CLcalc_DEVICE_gpu0 = 0;
    public final static int CLcalc_DEVICE_gpu1 = 1;
    public final static int CLcalc_DEVICE_cpu0 = 2;
    public final static int CLcalc_DEVICE_cpu1 = 3;
    public final static int CLcalc_DEVICE_gpu_fast = 4;
    public final static int CLcalc_DEVICE_cpu_fast = 5;

    static {
        //load the C-Library
        System.loadLibrary("CLcalc");
    }

    /**
     * Init the CLcalc-Library.
     * You have to call this function befor you
     * are able to use the Libray. It sets up the Devices etc.
     * @return 0 = no error else error
     */
    public static native int CLcalc_init();

    /**
     * You have to call this function to clean up the Memory of the GPU at the end
     * of your Programm
     * @return 0=no errors else error
     */
    public static native int CLcalc_shutdown();

    /**
     * Returns the Name of the Device or null if the Device is not available
     * @param device_type   the typ of device used: CLcalc_DEVICE_*
     * @return              a String containing the name of the Device
     */
    public static native String CLcalc_getDeviceName(int device_type);

    /**
     * Returns the Name of the Device or null if the Device is not available
     * @param device_type   the typ of device used: CLcalc_DEVICE_*
     * @return              Array: {MHz, cores, cores*MHz}
     */
    public static native int[] CLcalc_getDeviceSpeed(int device_type);

    //--------------------------------------------------------------------------------------------------
    //Interface to the Executor-Objects
    //--------------------------------------------------------------------------------------------------

    /**
     * Create a new Executor-Object
     * @param device_type   the typ of device used: CLcalc_DEVICE_*
     * @return              a Executor-ID
     */
    public static native int CLcalc_Executor_new(int device_type);

    /**
     * Add a Programm to execute, this directly builds a kernel. If the Program already exists
     * for this device(s), it is not build again.
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @param source        the Source of the program
     *                      The declaration of the kernel must be in the form:
     *                      <p>__kernel void <i>name</i> (__global float* <i>arg1</i>, __global float* <i>arg2</i>, const unsigned int <i>count</i>)</p>
     * @param name          the name of the kernel inside the Program
     * @return a progID witch refers to the kernel, not to the compiled program.
     * @throws Error 
     */
    public static native int CLcalc_Executor_addProgFromSource(int ExecutorID, String source, String name) throws Error;

    /**
     * Add an data to a Device
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @param data          the 1-dim float data array
     * @param data_dir      the memory typ of the 2nd argument: CL_MEM_*
     * @return              the id of this array inside the device
     * @throws Error
     */
    public static native int CLcalc_Executor_addData(int ExecutorID, Object data, long data_dir) throws Error;

    /**
     * Read Data from a device
     * The float[]-Object must be the same as the one used to call addData...
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @param DataID        the ID of the data array
     * @param data          the 1-dim float data array
     * @throws Error
     */
    public static native void CLcalc_Executor_getData(int ExecutorID, int DataID, Object data) throws Error;

    /**
     * release a used Momory-Object, that will cause the device to free the Memory
     * The Executor will call that function also inside of the shutdown function
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @param DataID        the ID of the data object
     * @param data          the data array to release
     * @throws Error        in case of any error
     */
    public static native void CLcalc_Executor_releaseData(int ExecutorID, int DataID, Object data) throws Error;

    /**
     * Assign Data to a Program as an argument
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @param progID        the ID of the Programm returned by addProgFrom...
     * @param arg_pos       the position of the argument (1st, 2nd, ...)
     * @param DataID        the ID of the Data-Object
     * @throws Error
     */
    public static native void CLcalc_Executor_setArgument(int ExecutorID, int progID, int arg_pos, int DataID) throws Error;

    /**
     * Execute the Kernel with the Index 0 to MaxIndex
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @param progID        the ID of the Programm returned by addProgFrom...
     * @param MaxIndex      the number of kernels to execute. Normaly this is the
     *                      number of elements in your input-array
     * @throws Error
     */
    public static native void CLcalc_Executor_Execute(int ExecutorID, int progID, int MaxIndex) throws Error;

    /**
     * Release everything that is attached to the executor, the ExecutorID is not longer valid
     * @param ExecutorID    the ID returned by CLcalc_Executor_new
     * @return              0 on success
     */
    public static native int CLcalc_Executor_shutdown(int ExecutorID);

    //--------------------------------------------------------------------------------------------------
    //Interface to the Build In Functions
    //--------------------------------------------------------------------------------------------------

    /**
     * Use one of the OpenCL intrinsic functions which have one argument
     * for example output[i] = sqrt(input[i])
     * @param ExecutorID    the ID of the Executor returned by CLcalc_Executor_new, or 0 for the default Executor
     * @param input         the input array
     * @param output        the output array
     * @param fname         the function to use, example: "sqrt"
     * @throws Error
     */
    public static native void CLcalc_Executor_BuildIn_CLintrinsics1arg(int ExecutorID, Object input, Object output, String fname) throws Error;

    /**
     * Use one of the OpenCL intrinsic functions which have one argument
     * for example output[i] = sqrt(input[i])
     * @param ExecutorID    the ID of the Executor returned by CLcalc_Executor_new, or 0 for the default Executor
     * @param inputID       the ID of the input array
     * @param input         the input array (only used to get the datatype)
     * @param outputID      the ID of the output array
     * @param output        the output array (only used to get the datatyp)
     * @param fname         the function to use, example: "sqrt"
     * @throws Error
     */
    public static native void CLcalc_Executor_BuildIn_CLintrinsics1arg_execOnly(int ExecutorID, int inputID, Object input, int outputID, Object output, String fname) throws Error;


    /**
     * Calculate the sum of each element of 2 arrays
     * @param ExecutorID    the ID of the Executor returned by CLcalc_Executor_new, or 0 for the default Executor
     * @param arg1          the 1st argument array
     * @param arg2          the 2nd argument array
     * @param result        the result array
     * @param fname         the operant, example: "+"
     * @throws Error
     */
    public static native void CLcalc_Executor_BuildIn_basic(int ExecutorID, Object arg1, Object arg2, Object result, String fname) throws Error;

    /**
     * Calculate the sum of each element of 2 arrays, no data tranfer is
     * will be done. The data must already be on the device.
     * @param ExecutorID    the ID of the Executor returned by CLcalc_Executor_new, or 0 for the default Executor
     * @param arg1ID        the ID of the 1st argument array
     * @param arg1          the argument 1 array (only used to get the datatype)
     * @param arg2ID        the ID of the 2nd argument array
     * @param arg2          the argument 2 array (only used to get the datatype)
     * @param resultID      the ID of the result array
     * @param result        the result array (only used to get the datatype)
     * @param fname         the operant, example: "+"
     */
    public static native void CLcalc_Executor_BuildIn_basic_execOnly(int ExecutorID, int arg1ID, Object arg1, int arg2ID, Object arg2, int resultID, Object result, String fname);

}
